﻿// ================================================================================
// The contents of this file are subject to the Mozilla Public License
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/2.0/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
// ================================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using Reflex.Framework;
using Reflex.Framework.Communication;
using Reflex.Framework.View;
using Reflex.Interface;
using Reflex.Interface.Aion;
using Reflex.Os.Aion.View;

namespace Reflex.Os.Aion.Service
{
	/// <summary>
	/// Represents the ChatService.
	/// </summary>
	[Export( typeof( IReflex ))]
	public class ChatService : ViewModelCommunication, IReflex
	{
		/// <summary>
		/// Contains the application instance.
		/// </summary>
		private IAionInstance _hAionInstance;

		/// <summary>
		/// Contains the action.
		/// </summary>
		private Action<IChat> _hAction;

		/// <summary>
		/// Contains the configuration window.
		/// </summary>
		private WindowView _hConfiguration;

		/// <summary>
		/// Contains each group conversation.
		/// </summary>
		private Dictionary<ChatType, WindowView> _hGroup;

		/// <summary>
		/// Contains each private conversation.
		/// </summary>
		private Dictionary<string, WindowView> _hPrivate;

		/// <summary>
		/// Contains the setting.
		/// </summary>
		private Setting _hSetting;

		/// <summary>
		/// Initialize a new instance of the ChatService class.
		/// </summary>
		public ChatService()
		{
			// Initialize a new instance of the Dictionary class.
			_hGroup = new Dictionary<ChatType, WindowView>();

			// Initialize a new instance of the Dictionary class.
			_hPrivate = new Dictionary<string, WindowView>();
		}

		#region Configuration
		/// <summary>
		/// Indicates whether Alliance chat is shown.
		/// </summary>
		public bool Alliance
		{
			get
			{
				// Retrieve the property.
				return PropertyGet<bool>(() => Alliance, true );
			}
			set
			{
				// Set the property.
				if ( PropertySet(() => Alliance, value ))
				{
					// Set the setting.
					_hSetting["Alliance"].Value = value.ToString();

					// Save the setting.
					_hSetting.Save();
				}
			}
		}

		/// <summary>
		/// Indicates whether Legion chat is shown.
		/// </summary>
		public bool Legion
		{
			get
			{
				// Retrieve the property.
				return PropertyGet<bool>(() => Legion, true );
			}
			set
			{
				// Set the property.
				if ( PropertySet(() => Legion, value ))
				{
					// Set the setting.
					_hSetting["Legion"].Value = value.ToString();

					// Save the setting.
					_hSetting.Save();
				}
			}
		}

		/// <summary>
		/// Indicates whether Group chat is shown.
		/// </summary>
		public bool Group
		{
			get
			{
				// Retrieve the property.
				return PropertyGet<bool>(() => Group, true );
			}
			set
			{
				// Set the property.
				if ( PropertySet(() => Group, value ))
				{
					// Set the setting.
					_hSetting["Group"].Value = value.ToString();

					// Save the setting.
					_hSetting.Save();
				}
			}
		}

		/// <summary>
		/// Indicates whether Normal chat is shown.
		/// </summary>
		public bool Normal
		{
			get
			{
				// Retrieve the property.
				return PropertyGet<bool>(() => Normal, true );
			}
			set
			{
				// Set the property.
				if ( PropertySet(() => Normal, value ))
				{
					// Set the setting.
					_hSetting["Normal"].Value = value.ToString();

					// Save the setting.
					_hSetting.Save();
				}
			}
		}
		
		/// <summary>
		/// Indicates whether Shout chat is shown.
		/// </summary>
		public bool Shout
		{
			get
			{
				// Retrieve the property.
				return PropertyGet<bool>(() => Shout, true );
			}
			set
			{
				// Set the property.
				if ( PropertySet(() => Shout, value ))
				{
					// Set the setting.
					_hSetting["Shout"].Value = value.ToString();

					// Save the setting.
					_hSetting.Save();
				}
			}
		}
		#endregion

		#region IDisposable
		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			// Invoke the delegate on the UI thread.
			Application.Current.Dispatcher.Invoke( new Action(() =>
			{
				// Lock this service.
				lock( this )
				{
					// Iterate while the group conversation dictionary has an item.
					while ( _hGroup.Count != 0 )
					{
						// Retrieve the item.
						ChatType eChatType = _hGroup.Keys.First();

						// Close the window.
						_hGroup[eChatType].Close();

						// Remove the item.
						_hGroup.Remove( eChatType );
					}

					// Iterate while the private conversation dictionary has an item.
					while ( _hPrivate.Count != 0 )
					{
						// Retrieve the item.
						string zName = _hPrivate.Keys.First();

						// Close the window.
						_hPrivate[zName].Close();

						// Remove the item.
						_hPrivate.Remove( zName );
					}

					// Remove the chat handler.
					_hAionInstance.Controller.Chat -= _hAction;
				}
			}));
		}
		#endregion

		#region IService
		/// <summary>
		/// Occurs when the button has been invoked.
		/// </summary>
		public Action Click
		{
			get
			{
				// Return the delegate.
				return () =>
				{
					// Invoke the delegate on the UI thread.
					Application.Current.Dispatcher.BeginInvoke( new Action(() =>
					{
						// Check if a window is to be created.
						if ( _hConfiguration == null || !_hConfiguration.IsVisible )
						{
							// Initialize a new instance of the ChatConfigurationView class.
							ChatConfigurationView hChatConfigurationView = new ChatConfigurationView();

							// Initialize a new instance of the ObservableCollection class.
							hChatConfigurationView.DataContext = this;

							// Set the window title.
							hChatConfigurationView.Title = string.Format( "Chat Configuration ({0})", _hAionInstance.Name );

							// Initialize a new instance of the WindowView class.
							_hConfiguration = new WindowView();

							// Set the data context.
							_hConfiguration.DataContext = hChatConfigurationView;

							// Show the window.
							_hConfiguration.Show();
						}
					}));
				};
			}
		}

		/// <summary>
		/// Contains the description.
		/// </summary>
		public string Description
		{
			get
			{
				// Return the description.
				return "Enables out-of-game chat.";
			}
		}

		/// <summary>
		/// Initialize the service.
		/// </summary>
		/// <param name="Instance">Contains the instance for which to provide a service.</param>
		/// <param name="Setting">Contains the setting.</param>
		public bool Initialize( IReflexServerInstance Instance, Setting Setting )
		{
			// Check if the value is an Aion application instance.
			if ( Instance is IAionInstance )
			{
				// Set the application instance.
				_hAionInstance = ( Instance as IAionInstance );

				// Initialize the action.
				_hAction = ( Chat ) =>
				{
					// Check if this message is a whisper.
					if ( Chat.Type == ChatType.Whisper )
					{
						// Invoke the delegate on the UI thread.
						Application.Current.Dispatcher.BeginInvoke( new Action(() =>
						{
							// Lock this service.
							lock( this )
							{
								// Retrieve the appropriate key.
								string Key = Chat.Receiver != null && Chat.Receiver.Equals( _hAionInstance.Name ) ? Chat.Sender : Chat.Receiver;

								// Change the key to proper capitalization.
								Key = Key.ToUpper()[0] + Key.Substring( 1 ).ToLower();

								// Check if the private conversation collection does not have the chat conversation.
								if ( !_hPrivate.ContainsKey( Key ))
								{
									// Initialize a new instance of the WindowView class.
									WindowView hWindowView = new WindowView();

									// Initialize a new instance of the ChatView class.
									ChatView hChatView = new ChatView();

									// Initialize a new instance of the ChatConversationService class.
									hChatView.DataContext = new ChatConversationService( _hAionInstance, Key );
								
									// Set the window title.
									hChatView.Title = string.Format( "{0} ({1})", Key, _hAionInstance.Name );

									// Set the data context.
									hWindowView.DataContext = hChatView;
				
									// Set the window state.
									hWindowView.WindowState = WindowState.Minimized;

									// Set the window in the private conversation collection.
									_hPrivate[Key] = hWindowView;
								}

								// Show the window.
								_hPrivate[Key].Show();

								// Add the chat message.
								(( _hPrivate[Key].DataContext as ChatView ).DataContext as ChatConversationService ).Message.Add( string.Format( "[{0}] {1}: {2}", DateTime.Now.ToString( "HH:mm" ), Chat.Sender, Chat.Message ));
							}
						}));
					}
					// Otherwise it is a group conversation.
					else if (( Alliance && Chat.Type == ChatType.Alliance ) || ( Group && Chat.Type == ChatType.Group ) || ( Legion && Chat.Type == ChatType.Legion ) || ( Normal && Chat.Type == ChatType.Normal ) || ( Shout && Chat.Type == ChatType.Shout ))
					{
						// Invoke the delegate on the UI thread.
						Application.Current.Dispatcher.BeginInvoke( new Action(() =>
						{
							// Check if the group conversation collection does not have the chat type.
							if ( !_hGroup.ContainsKey( Chat.Type ))
							{
								// Initialize a new instance of the WindowView class.
								WindowView hWindowView = new WindowView();

								// Initialize a new instance of the ChatView class.
								ChatView hChatView = new ChatView();

								// Initialize a new instance of the ChatConversationService class.
								hChatView.DataContext = new ChatConversationService( _hAionInstance, Chat.Type );
							
								// Set the window title.
								hChatView.Title = string.Format( "{0} ({1})", Chat.Type, _hAionInstance.Name );
									
								// Set the data context.
								hWindowView.DataContext = hChatView;
							
								// Set the window state.
								hWindowView.WindowState = WindowState.Minimized;

								// Set the window in the group conversation collection.
								_hGroup[Chat.Type] = hWindowView;
							}

							// Show the window.
							_hGroup[Chat.Type].Show();

							// Add the chat message.
							(( _hGroup[Chat.Type].DataContext as ChatView ).DataContext as ChatConversationService ).Message.Add( string.Format( "[{0}] {1}: {2}", DateTime.Now.ToString( "HH:mm" ), Chat.Sender, Chat.Message ));
						}));
					}
				};

				// Attempt the following code.
				try
				{
					// Initialize a boolean.
					bool bValue;

					// Set the setting.
					_hSetting = Setting;
					
					// Set the Alliance value.
					if ( bool.TryParse( _hSetting["Alliance"].Value, out bValue )) PropertySet(() => Alliance, bValue );
					
					// Set the Legion value.
					if ( bool.TryParse( _hSetting["Legion"].Value, out bValue )) PropertySet(() => Legion, bValue );
					
					// Set the Group value.
					if ( bool.TryParse( _hSetting["Group"].Value, out bValue )) PropertySet(() => Group, bValue );
					
					// Set the Normal value.
					if ( bool.TryParse( _hSetting["Normal"].Value, out bValue )) PropertySet(() => Normal, bValue );
					
					// Set the Shout value.
					if ( bool.TryParse( _hSetting["Shout"].Value, out bValue )) PropertySet(() => Shout, bValue );

					// Return true.
					return true;
				}
				// Catch an exception.
				catch
				{
					// Return true.
					return true;
				}
			}
			// Otherwise return false.
			else return false;
		}

		/// <summary>
		/// Indicates whether the service is enabled.
		/// </summary>
		public bool IsEnabled
		{
			get
			{
				// Retrieve the property.
				return PropertyGet<bool>(() => IsEnabled );
			}
			set
			{
				// Set the property.
				if ( PropertySet(() => IsEnabled, value ))
				{
					// Check if the property is enabled.
					if ( !value )
					{
						// Dispose of the service.
						Dispose();
					}
					// Otherwise the property is not enabled.
					else
					{
						// Occurs when a chat message has been received.
						_hAionInstance.Controller.Chat += _hAction;
					}
				}
			}
		}

		/// <summary>
		/// Indicates whether the service is a role.
		/// </summary>
		public bool IsRole
		{
			get
			{
				// Return false.
				return false;
			}
		}

		/// <summary>
		/// Contains the name.
		/// </summary>
		public string Name
		{
			get
			{
				// Return the name.
				return "Chat";
			}
		}
		#endregion
	}
}